package cn.flkj.lucia1a4a1.intermediate;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import org.hyperledger.fabric.protos.peer.Chaincode;
import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.exception.ChaincodeEndorsementPolicyParseException;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.exception.ProposalException;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @author : 州长在手 2021/1/25 上午10:33
 */
public class IntermediateLifecycleChaincode {
    private static final int proposalWaitTime = 20000;

    //-----------------------关于安装-----------------------关于安装-----------------------关于安装-----------------------

    private String chaincodeName ;
    private String chaincodeVersion ;
    private String chaincodeLabel ;
    private String chaincodeSourceLocation ;
    private String chaincodePath ;
    private String metadadataSource ;
    private String chaincodeEndorsementPolicyPath;
    private ChaincodeCollectionConfiguration chaincodeCollectionConfiguration ;
    private LifecycleChaincodeEndorsementPolicy chaincodeEndorsementPolicy;

    /**
     * 安装链码产生的packageID
     */
    // fabcar01042_1:825b254fb7feb28ad19013ba8c610a90edfcd3e94d295596aea7ebaf1ed723ea
    private String packageID;

    public IntermediateLifecycleChaincode() {
        try {
            chaincodeName = "fabcar010701";
            chaincodeVersion = "1";
            chaincodeLabel = "fabcar010701_1";
            chaincodeSourceLocation = "/home/ct/go";
            chaincodePath = "github.com/hyperledger/fabric-samples/chaincode/fabcar/go";
            metadadataSource = "/home/ct/IdeaProjects/fabric-sdk-java/src/test/fixture/meta-infs/end2endit";
            chaincodeEndorsementPolicy =
                    LifecycleChaincodeEndorsementPolicy.fromSignaturePolicyYamlFile(
                            Paths.get("/home/ct/IdeaProjects/fabric-sdk-java/src/test/fixture/sdkintegration/chaincodeendorsementpolicy.yaml"));
        } catch (Exception ignore) {
        }

    }

    public IntermediateLifecycleChaincode(String chaincodeName,
                                          String chaincodeVersion,
                                          String packageID){
        this.chaincodeName = chaincodeName;
        this.chaincodeVersion = chaincodeVersion;
        this.packageID = packageID;
    }

    public IntermediateLifecycleChaincode(String chaincodeName,
                                          String chaincodeVersion,
                                          String chaincodeLabel, // 标签，是名字加版本号的结合
                                          String chaincodeSourceLocation, // GOPATH
                                          String chaincodePath, // 相对于GOPATH的路径
                                          String metadadataSource, // metadadataSource,一般是couchdb的连接方式JSON的路径
                                          String chaincodeEndorsementPolicyPath, // 策略文件
                                          ChaincodeCollectionConfiguration chaincodeCollectionConfiguration //字符串代码集合配置,可能是私有数据
    ) throws IOException, ChaincodeEndorsementPolicyParseException {
        this.chaincodeName = chaincodeName;
        this.chaincodeVersion = chaincodeVersion;
        this.chaincodeLabel = chaincodeLabel;
        this.chaincodeSourceLocation = chaincodeSourceLocation;
        this.chaincodePath = chaincodePath;
        this.metadadataSource = metadadataSource;
        this.chaincodeEndorsementPolicyPath = chaincodeEndorsementPolicyPath;
        this.chaincodeCollectionConfiguration = chaincodeCollectionConfiguration;
        chaincodeEndorsementPolicy = LifecycleChaincodeEndorsementPolicy.fromSignaturePolicyYamlFile(
                Paths.get(this.chaincodeEndorsementPolicyPath));
    }
    /**
     * 安装智能合约,包含打包和上传到peer节点
     */

    public String install(IntermediateOrg org) throws ProposalException, InvalidArgumentException, IOException {
        Path metadataSourcePath = null;
        if (metadadataSource != null) {
            metadataSourcePath = Paths.get(metadadataSource);
        }
        LifecycleChaincodePackage lifecycleChaincodePackage = LifecycleChaincodePackage.fromSource(
                chaincodeLabel,
                Paths.get(chaincodeSourceLocation),
                TransactionRequest.Type.GO_LANG,
                chaincodePath, metadataSourcePath);
        //org1 peer 节点的集合
        Collection<Peer> peersOrgx = new LinkedList<>();
        // 获取加入同一个通道的所有peer，根据org区分开
        for (Peer peer : org.getChannel().get().getPeers()) {
            if (peer.getUrl().contains(org.getOrgName())) {
                peersOrgx.add(peer);
            }
        }
        packageID = lifecycleInstallChaincode(org.getClient(), peersOrgx, lifecycleChaincodePackage);
        return packageID;
    }

    public String install(Map<String ,IntermediateOrg> orgs) throws ProposalException, InvalidArgumentException, IOException {
        Path metadataSourcePath = null;
        if (metadadataSource != null) {
            metadataSourcePath = Paths.get(metadadataSource);
        }
        LifecycleChaincodePackage lifecycleChaincodePackage = LifecycleChaincodePackage.fromSource(chaincodeLabel, Paths.get(chaincodeSourceLocation),
                TransactionRequest.Type.GO_LANG,
                chaincodePath, metadataSourcePath);
        for(IntermediateOrg org : orgs.values()){
            //org1 peer 节点的集合
            Collection<Peer> peersOrgx = new LinkedList<>();
            for (Peer peer : org.getChannel().get().getPeers()) {
                if (peer.getUrl().contains(org.getOrgName())) {
                    peersOrgx.add(peer);
                }
            }
            packageID = lifecycleInstallChaincode(org.getClient(), peersOrgx, lifecycleChaincodePackage);
        }
        return packageID;
    }

    /**
     * 审批链码
     */
    public boolean approveformyorg(IntermediateOrg org) throws ProposalException, InvalidArgumentException, InterruptedException, ExecutionException, TimeoutException {
        BlockEvent.TransactionEvent transactionEvent = lifecycleApproveChaincodeDefinitionForMyOrg(org)
                .get(20000, TimeUnit.SECONDS);
        return transactionEvent.isValid();
    }

    public boolean approveformyorg(Map<String ,IntermediateOrg> orgs) throws Exception {

        try {
            for (IntermediateOrg org : orgs.values()){
                LifecycleApproveChaincodeDefinitionForMyOrgRequest
                        lifecycleApproveChaincodeDefinitionForMyOrgRequest = //  好长的类型
                        org.getClient().newLifecycleApproveChaincodeDefinitionForMyOrgRequest();
                lifecycleApproveChaincodeDefinitionForMyOrgRequest.setSequence(1);
                lifecycleApproveChaincodeDefinitionForMyOrgRequest.setChaincodeName(chaincodeName);
                lifecycleApproveChaincodeDefinitionForMyOrgRequest.setChaincodeVersion(chaincodeVersion);
                lifecycleApproveChaincodeDefinitionForMyOrgRequest.setInitRequired(true);
                if (null != chaincodeCollectionConfiguration) {
                    lifecycleApproveChaincodeDefinitionForMyOrgRequest.
                            setChaincodeCollectionConfiguration(chaincodeCollectionConfiguration);
                }
                if (null != chaincodeEndorsementPolicy) {
                    lifecycleApproveChaincodeDefinitionForMyOrgRequest.
                            setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);
                }
                lifecycleApproveChaincodeDefinitionForMyOrgRequest.setPackageId(packageID);

                //org1 peer 节点的集合
                Collection<Peer> peersOrgx = new LinkedList<>();
                // 获取加入同一个通道的所有peer，根据org区分开
                for (Peer peer : org.getChannel().get().getPeers()) {
                    if (peer.getUrl().contains(org.getOrgName())) {
                        peersOrgx.add(peer);
                    }
                }
                Collection<LifecycleApproveChaincodeDefinitionForMyOrgProposalResponse>
                        lifecycleApproveChaincodeDefinitionForMyOrgProposalResponse =
                        org.getChannel().get().sendLifecycleApproveChaincodeDefinitionForMyOrgProposal(
                                lifecycleApproveChaincodeDefinitionForMyOrgRequest, peersOrgx);
                org.getChannel().get().sendTransaction(lifecycleApproveChaincodeDefinitionForMyOrgProposalResponse);
            }
            return true;
        }catch (Exception e){
            throw new Exception(e);
        }


//        BlockEvent.TransactionEvent transactionEvent = lifecycleApproveChaincodeDefinitionForMyOrg(org)
//                .get(20000, TimeUnit.SECONDS);
//        return transactionEvent.isValid();
    }

    /**
     * 提交链码
     */
    public void commit(IntermediateOrg org) throws InvalidArgumentException, InterruptedException, ExecutionException, TimeoutException, ProposalException {
        commitChaincodeDefinitionRequest(org);
    }

    /**
     *  2.2.1的链码默认不会初始化，我们在这里手动执行
     *  默认执行 init
     */

    public void initChaincode(IntermediateOrg org ,String initFunc ,String...args) throws ProposalException, InvalidArgumentException {

        TransactionProposalRequest transactionProposalRequest = org.getClient().newTransactionProposalRequest();
        transactionProposalRequest.setChaincodeName(chaincodeName);
        transactionProposalRequest.setChaincodeLanguage( TransactionRequest.Type.GO_LANG);
        transactionProposalRequest.setUserContext(org.getUser(org.getUsername()));
        transactionProposalRequest.setFcn(initFunc);
        transactionProposalRequest.setProposalWaitTime(20000);
        transactionProposalRequest.setArgs(args);
        transactionProposalRequest.setInit(true);

        //org1 peer 节点的集合
        Collection<Peer> peersOrgx = new LinkedList<>();
        // 获取加入同一个通道的所有peer，根据org区分开,只要一个
        for (Peer peer : org.getChannel().get().getPeers()) {
            if (peer.getUrl().contains(org.getOrgName())) {
                peersOrgx.add(peer);
                break;
            }
        }
        Collection<ProposalResponse> transactionPropResp = org.getChannel().get().sendTransactionProposal(transactionProposalRequest,peersOrgx);
        Collection<ProposalResponse> successful = new LinkedList<>();
        for (ProposalResponse response : transactionPropResp) {
            if (response.getStatus() == ProposalResponse.Status.SUCCESS) {
                successful.add(response);
            }
        }
        org.getChannel().get().sendTransaction(successful);
    }

    private void commitChaincodeDefinitionRequest(IntermediateOrg org) throws InvalidArgumentException, ProposalException {

        LifecycleCommitChaincodeDefinitionRequest
                lifecycleCommitChaincodeDefinitionRequest = org.getClient().newLifecycleCommitChaincodeDefinitionRequest();
        lifecycleCommitChaincodeDefinitionRequest.setSequence(1);
        lifecycleCommitChaincodeDefinitionRequest.setChaincodeName(chaincodeName);
        lifecycleCommitChaincodeDefinitionRequest.setChaincodeVersion(chaincodeVersion);
        lifecycleCommitChaincodeDefinitionRequest.setInitRequired(true);
        if (null != chaincodeEndorsementPolicy) {
            lifecycleCommitChaincodeDefinitionRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);
        }
        if (null != chaincodeCollectionConfiguration) {
            lifecycleCommitChaincodeDefinitionRequest.setChaincodeCollectionConfiguration(chaincodeCollectionConfiguration);
        }
        lifecycleCommitChaincodeDefinitionRequest.setInitRequired(true);

        //org1 peer 节点的集合
        Collection<Peer> peersOrgx = new LinkedList<>();
        // 获取加入同一个通道的所有peer，根据org区分开
        for (Peer peer : org.getChannel().get().getPeers()) {
            if (peer.getUrl().contains(org.getOrgName())) {
                peersOrgx.add(peer);
            }
        }
        Collection<LifecycleCommitChaincodeDefinitionProposalResponse>
                lifecycleCommitChaincodeDefinitionProposalResponses =
                org.getChannel().get().sendLifecycleCommitChaincodeDefinitionProposal(
                        lifecycleCommitChaincodeDefinitionRequest,
                        peersOrgx);
        try {
            org.getChannel().get().sendTransaction(lifecycleCommitChaincodeDefinitionProposalResponses).get(32000, TimeUnit.SECONDS);

        }catch (Exception e){
            System.out.println("Lifecy cleCommit >>>> "  + e.getMessage());
        }

    }

    private CompletableFuture<BlockEvent.TransactionEvent> lifecycleApproveChaincodeDefinitionForMyOrg(IntermediateOrg org)
            throws InvalidArgumentException, ProposalException {
        LifecycleApproveChaincodeDefinitionForMyOrgRequest
                lifecycleApproveChaincodeDefinitionForMyOrgRequest = //  好长的类型
                org.getClient().newLifecycleApproveChaincodeDefinitionForMyOrgRequest();
        lifecycleApproveChaincodeDefinitionForMyOrgRequest.setSequence(1);
        lifecycleApproveChaincodeDefinitionForMyOrgRequest.setChaincodeName(chaincodeName);
        lifecycleApproveChaincodeDefinitionForMyOrgRequest.setChaincodeVersion(chaincodeVersion);
        lifecycleApproveChaincodeDefinitionForMyOrgRequest.setInitRequired(true);
        if (null != chaincodeCollectionConfiguration) {
            lifecycleApproveChaincodeDefinitionForMyOrgRequest.
                    setChaincodeCollectionConfiguration(chaincodeCollectionConfiguration);
        }
        if (null != chaincodeEndorsementPolicy) {
            lifecycleApproveChaincodeDefinitionForMyOrgRequest.
                    setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);
        }
        lifecycleApproveChaincodeDefinitionForMyOrgRequest.setPackageId(packageID);

        //org1 peer 节点的集合
        Collection<Peer> peersOrgx = new LinkedList<>();
        // 获取加入同一个通道的所有peer，根据org区分开
        for (Peer peer : org.getChannel().get().getPeers()) {
            if (peer.getUrl().contains(org.getOrgName())) {
                peersOrgx.add(peer);
            }
        }
        Collection<LifecycleApproveChaincodeDefinitionForMyOrgProposalResponse>
                lifecycleApproveChaincodeDefinitionForMyOrgProposalResponse =
                org.getChannel().get().sendLifecycleApproveChaincodeDefinitionForMyOrgProposal(
                        lifecycleApproveChaincodeDefinitionForMyOrgRequest, peersOrgx);
        return org.getChannel().get().sendTransaction(lifecycleApproveChaincodeDefinitionForMyOrgProposalResponse);
    }

    private String lifecycleInstallChaincode(HFClient client, Collection<Peer> peers, LifecycleChaincodePackage lifecycleChaincodePackage) throws InvalidArgumentException, ProposalException {

        LifecycleInstallChaincodeRequest installProposalRequest = client.newLifecycleInstallChaincodeRequest();
        installProposalRequest.setLifecycleChaincodePackage(lifecycleChaincodePackage);
        installProposalRequest.setProposalWaitTime(200000);
        Collection<LifecycleInstallChaincodeProposalResponse> responses = client.sendLifecycleInstallChaincodeRequest(installProposalRequest, peers);
        String packageID = null;
        for (LifecycleInstallChaincodeProposalResponse response : responses) {
            if (response.getStatus() == ProposalResponse.Status.SUCCESS) {
                if (packageID == null) {
                    packageID = response.getPackageId();
                }
            }
        }
        return packageID;
    }




    //-----------------------关于执行-----------------------关于执行-----------------------关于执行-----------------------

    /**
     * 执行智能合约
     *
     * @param fcn  方法名
     * @param args 参数数组
     */
    public static JSONObject  invoke(HFClient client, Channel channel, User userContext, String chaincodeName, String fcn, String[] args) throws InvalidArgumentException, ProposalException, IOException {
        TransactionProposalRequest transactionProposalRequest = client.newTransactionProposalRequest();
        transactionProposalRequest.setChaincodeName(chaincodeName);
        transactionProposalRequest.setChaincodeLanguage(TransactionRequest.Type.GO_LANG);
        transactionProposalRequest.setUserContext(userContext);
        transactionProposalRequest.setFcn(fcn);
        transactionProposalRequest.setProposalWaitTime(proposalWaitTime);
        transactionProposalRequest.setArgs(args);
        Collection<ProposalResponse> transactionPropResp = channel.sendTransactionProposal(transactionProposalRequest, channel.getPeers());
        return toOrdererResponse(transactionPropResp, channel);
    }

    /**
     * 查询智能合约
     */
    public static JSONObject query(HFClient client, Channel channel,String chaincodeName,String fcn, String... args) throws InvalidArgumentException, ProposalException {
        QueryByChaincodeRequest queryByChaincodeRequest = client.newQueryProposalRequest();
        queryByChaincodeRequest.setArgs(args);
        queryByChaincodeRequest.setFcn(fcn);
        queryByChaincodeRequest.setChaincodeName(chaincodeName);
        Collection<ProposalResponse> queryProposals = channel.queryByChaincode(queryByChaincodeRequest, channel.getPeers());
        return toPeerResponse(queryProposals, true);
    }

    //-----------------------内部工具-----------------------内部工具-----------------------内部工具-----------------------

    /**
     * 获取实例化合约、升级合约以及invoke合约的返回结果集合
     *
     * @param proposalResponses 请求返回集合
     */
    private static JSONObject toOrdererResponse(Collection<ProposalResponse> proposalResponses, Channel channel) throws InvalidArgumentException, UnsupportedEncodingException {
        JSONObject jsonObject = new JSONObject();
        Collection<ProposalResponse> successful = new LinkedList<>();
        Collection<ProposalResponse> failed = new LinkedList<>();
        for (ProposalResponse response : proposalResponses) {
            if (response.getStatus() == ProposalResponse.Status.SUCCESS) {
                successful.add(response);
            } else {
                failed.add(response);
            }
        }
        if (failed.size() > 0) {
            ProposalResponse firstTransactionProposalResponse = failed.iterator().next();
            jsonObject.put("code", Utils.ERROR);
            jsonObject.put("error", firstTransactionProposalResponse.getMessage());
            return jsonObject;
        } else {
            ProposalResponse resp = proposalResponses.iterator().next();
            byte[] x = resp.getChaincodeActionResponsePayload();
            String resultAsString = null;
            if (x != null) {
                resultAsString = new String(x, "UTF-8");
            }
            channel.sendTransaction(successful);
            jsonObject = parseResult(resultAsString);
            jsonObject.put("code", Utils.SUCCESS);
            jsonObject.put("txid", resp.getTransactionID());
            return jsonObject;
        }
    }

    /**
     * 获取安装合约以及query合约的返回结果集合
     *
     * @param proposalResponses 请求返回集合
     * @param checkVerified     是否验证提案
     */
    private static JSONObject toPeerResponse(Collection<ProposalResponse> proposalResponses, boolean checkVerified) {
        JSONObject jsonObject = new JSONObject();
        for (ProposalResponse proposalResponse : proposalResponses) {
            if ((checkVerified && !proposalResponse.isVerified()) || proposalResponse.getStatus() != ProposalResponse.Status.SUCCESS) {
                String data = String.format("Failed install/query proposal from peer %s status: %s. Messages: %s. Was verified : %s",
                        proposalResponse.getPeer().getName(), proposalResponse.getStatus(), proposalResponse.getMessage(), proposalResponse.isVerified());
                jsonObject.put("code", Utils.ERROR);
                jsonObject.put("error", data);
            } else {
                String payload = proposalResponse.getProposalResponse().getResponse().getPayload().toStringUtf8();
                jsonObject = parseResult(payload);
                jsonObject.put("code", Utils.SUCCESS);
                jsonObject.put("txid", proposalResponse.getTransactionID());
            }
        }
        return jsonObject;
    }

    private static JSONObject parseResult(String result) {
        JSONObject jsonObject = new JSONObject();
        int jsonVerify = isJSONValid(result);
        switch (jsonVerify) {
            case 0:
                jsonObject.put("data", result);
                break;
            case 1:
                jsonObject.put("data", JSONObject.parseObject(result));
                break;
            case 2:
                jsonObject.put("data", JSONObject.parseArray(result));
                break;
        }
        return jsonObject;
    }

    /**
     * 判断字符串类型
     *
     * @param str 字符串
     * @return 0-string；1-JsonObject；2、JsonArray
     */
    private static int isJSONValid(String str) {
        try {
            JSONObject.parseObject(str);
            return 1;
        } catch (JSONException ex) {
            try {
                JSONObject.parseArray(str);
                return 2;
            } catch (JSONException ex1) {
                return 0;
            }
        }
    }

}
